using Microsoft.Extensions.DependencyInjection;
using SanJ.FreeCare.Abp.Authorization.Users;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Services;
using Volo.Abp.Features;
using Volo.Abp.SettingManagement;

namespace SanJ.FreeCare.Notifications
{
    /// <summary>
    /// Implements <see cref="INotificationDefinitionManager"/>.
    /// </summary>
    internal class NotificationDefinitionManager : DomainService, INotificationDefinitionManager, ISingletonDependency
    {
        private readonly INotificationConfiguration _configuration;
        private readonly IDictionary<string, NotificationDefinition> _notificationDefinitions;
        private readonly Lazy<List<ISettingManagementProvider>> _lazyProviders;

        public NotificationDefinitionManager(
            INotificationConfiguration configuration)
        {
            _configuration = configuration;
            _notificationDefinitions = new Dictionary<string, NotificationDefinition>();
        }

        public void Initialize()
        {
            var context = new NotificationDefinitionContext(this);

            foreach (var providerType in _configuration.Providers)
            {
                var provider = LazyServiceProvider.LazyGetRequiredService(providerType) as NotificationProvider;
                provider.SetNotifications(context);
            }
        }

        public void Add(NotificationDefinition notificationDefinition)
        {
            if (_notificationDefinitions.ContainsKey(notificationDefinition.Name))
            {
                throw new AbpInitializationException("There is already a notification definition with given name: " + notificationDefinition.Name + ". Notification names must be unique!");
            }

            _notificationDefinitions[notificationDefinition.Name] = notificationDefinition;
        }

        public NotificationDefinition Get(string name)
        {
            var definition = GetOrNull(name);
            if (definition == null)
            {
                throw new AbpException("There is no notification definition with given name: " + name);
            }

            return definition;
        }

        public NotificationDefinition GetOrNull(string name)
        {
            return _notificationDefinitions.GetOrDefault(name);
        }

        public void Remove(string name)
        {
            _notificationDefinitions.Remove(name);
        }

        public IReadOnlyList<NotificationDefinition> GetAll()
        {
            return _notificationDefinitions.Values.ToImmutableList();
        }

        public async Task<bool> IsAvailableAsync(string name, UserIdentifier user)
        {
            var notificationDefinition = GetOrNull(name);
            if (notificationDefinition == null)
            {
                return true;
            }

            if (notificationDefinition.FeatureDependency != null)
            {
                var featureChecker = LazyServiceProvider.LazyGetRequiredService<FeatureChecker>();
                try
                {
                    await featureChecker.CheckEnabledAsync(true, notificationDefinition.FeatureDependency);
                }
                catch (Exception)
                {

                    return false;
                }
            }

            if (notificationDefinition.PermissionDependency != null)
            {
                var permissionChecker = LazyServiceProvider.LazyGetRequiredService<PermissionChecker>();
                var result = await permissionChecker.IsGrantedAsync(notificationDefinition.PermissionDependency);
                if (!result.AllGranted)
                { return false; }
            }

            return true;
        }

        public async Task<IReadOnlyList<NotificationDefinition>> GetAllAvailableAsync(UserIdentifier user)
        {
            var availableDefinitions = new List<NotificationDefinition>();

            var permissionChecker = LazyServiceProvider.LazyGetRequiredService<PermissionChecker>();
            var featureChecker = LazyServiceProvider.LazyGetRequiredService<FeatureChecker>();


            foreach (var notificationDefinition in GetAll())
            {
                var result = await permissionChecker.IsGrantedAsync(notificationDefinition.PermissionDependency);
                if (notificationDefinition.PermissionDependency != null && !result.AllGranted)
                {
                    continue;
                }
                bool isFeatureEnable = true;
                try
                {
                    await featureChecker.CheckEnabledAsync(true, notificationDefinition.FeatureDependency);
                }
                catch (Exception)
                {

                    isFeatureEnable = false;
                }

                if (user.TenantId.HasValue &&
                    notificationDefinition.FeatureDependency != null && !isFeatureEnable)
                {
                    continue;
                }

                availableDefinitions.Add(notificationDefinition);
            }

            return availableDefinitions.ToImmutableList();
        }

    }
}